package gbench.appdemo.ggplot.plot.panel;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.List;
import java.util.Objects;
import java.util.stream.Stream;

import gbench.common.matlib.MatlibCanvas.NVec;
import gbench.common.matlib.matrix.MatrixOps;
import gbench.common.matlib.matrix.MatrixOps.Tuple2;
import gbench.appdemo.ggplot.layer.Layer;

import static gbench.common.matlib.matrix.MatrixOps.stm;

/**
 * 绘图panel是一个独立的绘图区<br>
 * 承载绘图数据:pixels像素矩阵<br>
 * @author gbench
 *
 */
public class SinkPanel {

    /**
     * 
     * @param width
     * @param height
     */
    public SinkPanel(int width, int height, NVec af) {
        this.width = width;
        this.height = height;
        pixels = new int[width][height][];
        this.af = af;
    }
    
    /**
     * 
     * @param width
     * @param height
     */
    public SinkPanel(int width, int height) {
        this(width,height,null);
    }

    /**
     * 绘图操作，渲染成像素节点
     * 
     * @return render
     */
    public Stream<int[]> render() {
       
            final var shape = MatrixOps.shape(pixels);// 提取像素信息
            final var points = stm(shape._1()).flatMap(i -> stm(shape._2()).map(j -> {
                return pixels[i][j] == null ? null : new int[] { i, j, pixels[i][j][0], pixels[i][j][1], pixels[i][j][2] };
            })).filter(Objects::nonNull);
            
       return points;
    }
    
    /**
     * 绘图操作（转手过滤一下）pointS
     * 
     * @return render
     */
    public Stream<int[]> render(Stream<int[]> pointS) {
       // 转手过滤一下
       return Stream.concat(this.render(),pointS);
    }

    /**
     * 
     * @param nvec
     * @return
     */
    public SinkPanel addPoint(NVec nvec) {
        return this.addPoint(nvec.ints());
    }

    /**
     * 
     * @param nvec
     * @return
     */
    public SinkPanel addPoint(int[] aa) {
        pixels[aa[0]][aa[1]] = Arrays.copyOfRange(aa, 2, aa.length);
        return this;
    }

    /**
     * 添加元素点
     * 
     * @param nvec
     * @return
     */
    public SinkPanel addPoint(int x, int y, int[] c) {
        if (x >= 0 && x < width && y >= 0 && y < height) {
            pixels[x][y] = c;
        }
        return this;
    }

    /**
     * 添加元素点:对,x,y 进行translateX
     * 
     * @param x 水平坐标
     * @param y 垂直坐标
     * @param c 颜色像素
     * @return PlotPanel
     */
    public SinkPanel addPoint2(Double x, Double y, int[] c) {
        return this.addPoint(this.translateX(x), this.translateY(y), c);
    }
    
    /**
     * 添加元素点:对,,x,y 进行translateX
     * 
     * @param tup 水平坐标y ,垂直坐标
     * @param c    颜色像素 rgb 数组
     * @return PlotPanel
     */
    public SinkPanel addPoint2(Tuple2<? extends Number, ? extends Number> tup, int[] c) {
        return this.addPoint2(tup._1().doubleValue(), tup._2().doubleValue(), c);
    }

    /**
     * @return the width
     */
    public int getWidth() {
        return width;
    }

    /**
     * @param width the width to set
     */
    public void setWidth(int width) {
        this.width = width;
    }

    /**
     * @return the height
     */
    public int getHeight() {
        return height;
    }

    /**
     * @param height the height to set
     */
    public void setHeight(int height) {
        this.height = height;
    }

    /**
     * @return the pixels
     */
    public int[][][] getPixels() {
        return pixels;
    }

    /**
     * @param pixels the pixels to set
     */
    public void setPixels(int[][][] pixels) {
        this.pixels = pixels;
    }

    public int translateX(Number x) {
        final var _x = (int) (af.get(0) * x.doubleValue() + af.get(2)); // 水平坐标
        return _x;
    }

    public int translateY(Number y) {
        final var _x = (int) (af.get(1) * y.doubleValue() + af.get(3)); // 水平坐标
        return _x;
    }

    public SinkPanel affine(NVec af) {
        this.af = af;
        return this;
    }

    /**
     * 
     * @param layer
     * @return
     */
    public SinkPanel add(Layer layer) {
        layers.add(layer);
        return this;
    }

    /**
     * 
     * @param layers 绘图层
     * @return
     */
    public SinkPanel addAll(Collection<Layer> layers) {
        layers.addAll(layers);
        return this;
    }

    private List<Layer> layers = new ArrayList<Layer>();

    /**
     * @return the layers
     */
    public List<Layer> getLayers() {
        return layers;
    }

    /**
     * @param layers the layers to set
     */
    public void setLayers(List<Layer> layers) {
        this.layers = layers;
    }
    
    /**
     * 返回画板尺寸
     * @return
     */
    public int[] shape() {
        return new int[] {width,height};
    }

    private NVec af;
    private int width;
    private int height;
    private int[][][] pixels;

}
